use super::{Firework, FireworkState};

const SEED: u64 = 8675309;
const TERM_HEIGHT: u16 = 24;

#[test]
fn test_gen_state_constant() {
  // make sure it's consistent every time with a seeed
  let rng = rand::SeedableRng::seed_from_u64(SEED);
  let state_machine = Firework::new(rng, TERM_HEIGHT);

  let initial_state = state_machine.total_state.collect::<Vec<FireworkState>>();
  for _ in 0..100 {
    let rng = rand::SeedableRng::seed_from_u64(SEED);
    let state_machine = Firework::new(rng, TERM_HEIGHT);

    assert_eq!(
      state_machine.total_state.collect::<Vec<FireworkState>>(),
      initial_state
    );
  }
}

#[test]
fn test_firework_flies_properly() {
  let rng = rand::SeedableRng::seed_from_u64(SEED);
  let mut state_machine = Firework::new(rng, TERM_HEIGHT);
  let center = state_machine.center.position;

  assert_eq!(
    state_machine.tail_points.tail[0],
    state_machine.center.position
  );

  state_machine.advance();
  let to_test = vec![center.move_point(0, 1), center];
  for (idx, point) in state_machine.tail_points.tail.iter().enumerate() {
    assert_eq!(point.x, to_test[idx].x);
    assert_eq!(point.y, to_test[idx].y);
  }

  state_machine.advance();
  let to_test = vec![center.move_point(0, 2), center.move_point(0, 1), center];
  for (idx, point) in state_machine.tail_points.tail.iter().enumerate() {
    assert_eq!(point.x, to_test[idx].x);
    assert_eq!(point.y, to_test[idx].y);
  }

  state_machine.advance();
  let to_test = vec![
    center.move_point(0, 3),
    center.move_point(0, 2),
    center.move_point(0, 1),
    center,
  ];
  for (idx, point) in state_machine.tail_points.tail.iter().enumerate() {
    assert_eq!(point.x, to_test[idx].x);
    assert_eq!(point.y, to_test[idx].y);
  }
}

#[test]
fn test_firework_explodes_properly() {
  let rng = rand::SeedableRng::seed_from_u64(SEED);
  let mut state_machine = Firework::new(rng, TERM_HEIGHT);
  while state_machine.advance() != FireworkState::Exploding {}

  assert_eq!(state_machine.exploding_points.perpendicular_tips.len(), 4);
  assert_eq!(
    state_machine.exploding_points.diagonal_tips.len(),
    0,
    "Diagonal tips must only be set on even intervals"
  );

  let old_tail = state_machine.tail_points.tail.clone();
  let tail_size = old_tail.len();
  let tail_last = old_tail.last().unwrap();
  state_machine.advance();
  assert_eq!(state_machine.exploding_points.perpendicular_tips.len(), 4);
  assert_eq!(state_machine.exploding_points.diagonal_tips.len(), 4);
  assert!(state_machine.tail_points.tail.len() + 1 == tail_size);
  assert!(!state_machine.tail_points.tail.contains(tail_last));

  let diagonal_tips = state_machine.exploding_points.diagonal_tips.clone();
  state_machine.advance();
  assert_eq!(state_machine.exploding_points.diagonal_tips, diagonal_tips);
  assert_eq!(state_machine.exploding_points.explosion.len(), 16);
  state_machine
    .exploding_points
    .perpendicular_tips
    .iter()
    .chain(state_machine.exploding_points.diagonal_tips.iter())
    .for_each(|tip| assert!(state_machine.exploding_points.explosion.contains(tip)));
}

#[test]
fn test_firework_falls_properly() {
  let rng = rand::SeedableRng::seed_from_u64(SEED);
  let mut state_machine = Firework::new(rng, TERM_HEIGHT);
  while state_machine.advance() != FireworkState::Falling {}

  let exploding_points_len = state_machine.exploding_points.explosion.len();
  state_machine.advance();
  assert_eq!(
    state_machine.exploding_points.explosion.len(),
    exploding_points_len - 4
  );
}

#[test]
fn test_firework_fades_properly() {
  let rng = rand::SeedableRng::seed_from_u64(SEED);
  let mut state_machine = Firework::new(rng, TERM_HEIGHT);
  while state_machine.advance() != FireworkState::Fading {}
  let mut falling_len = state_machine.falling_points.falling.len();

  state_machine.advance();
  assert_eq!(falling_len - 8, state_machine.falling_points.falling.len());
  falling_len = state_machine.falling_points.falling.len();

  state_machine.advance();
  assert_eq!(falling_len - 4, state_machine.falling_points.falling.len());
}
